"""
# ------------------------------------------------------------------------------------------------------------------
# 在目标检测中一个很重要的问题就是NMS及IOU计算，而一般所说的目标检测检测的box是规则矩形框，计算IOU也非常简单，有两种方法：
# 1. 两个矩形的宽之和减去组合后的矩形的宽就是重叠矩形的宽，同比重叠矩形的高
# IOU = 交集部分/包含两个四边形最小多边形的面积
# 2. 右下角的minx减去左上角的maxx就是重叠矩形的宽，同比高
# IOU = 重叠面积 / （两矩形面积和—重叠面积）
# 不规则四边形就不能通过这种方式来计算，python的shapely包可以直接做到，下面给出的代码和注释
# 来自：白翔老师的textBoxes++论文源码，
# ------------------------------------------------------------------------------------------------------------------
reference: code IoU and bbox_iou_eval from the reference https://bbs.huaweicloud.com/blogs/357095
author: xueqin.xq
date: 2024.9.14
"""

import numpy as np
import shapely
from shapely.geometry import Polygon, MultiPoint, mapping  # 多边形

def polygon_iou(predict_polygon, groundtruth_polygon):
    """
    line1 = [2, 0, 2, 2, 0, 0, 0, 2]  # 四边形四个点坐标的一维数组表示，[x,y,x,y....]；随意分别放入框的四个角坐标
    line2 = [1, 1, 4, 1, 4, 4, 1, 4]
    a = np.array(line1).reshape(4, 2)  # 四边形二维坐标表示
    b = np.array(line2).reshape(4, 2)
    """
    a = np.array(predict_polygon).reshape(len(predict_polygon) >> 1, 2)  # 四边形二维坐标表示
    poly1 = Polygon(a).convex_hull  # python四边形对象，会自动计算四个点，最后四个点顺序为：左上 左下 右下 右上 左上
    #print(Polygon(a).convex_hull)  # 可以打印看看是不是这样子(0 0, 0 2, 2 2, 2 0, 0 0)
    b = np.array(groundtruth_polygon).reshape(len(groundtruth_polygon) >> 1, 2)
    poly2 = Polygon(b).convex_hull
    #print(Polygon(b).convex_hull)
    union_poly = np.concatenate((a, b))  # 合并两个box坐标，变为8*2
    #print(union_poly)
    #print(MultiPoint(union_poly).convex_hull)  # 包含两四边形最小的多边形点;(0 0, 0 2, 1 4, 4 4, 4 1, 2 0, 0 0)
    if not poly1.intersects(poly2):  # 如果两四边形不相交
        iou = 0
    else:
        try:
            inter_area = poly1.intersection(poly2).area  # 相交面积
            #print(inter_area)
            # union_area = poly1.area + poly2.area - inter_area
            union_area = MultiPoint(union_poly).convex_hull.area  # 最小多边形点面积
            #print(union_area)
            if union_area == 0:
                iou = 0
            # iou = float(inter_area) / (union_area-inter_area) #错了
            iou = float(inter_area) / union_area
            # iou=float(inter_area) /(poly1.area+poly2.area-inter_area)
            # 源码中给出了两种IOU计算方式，第一种计算的是: 交集部分/包含两个四边形最小多边形的面积
            # 第二种： 交集 / 并集（常见矩形框IOU计算方式）
        except shapely.geos.TopologicalError:
            print('shapely.geos.TopologicalError occured, iou set to 0')
            iou = 0
    #print("a: ", a)
    #print("b: ", b)
    #print("iou: ", iou)
    return iou

def IoU(box1, box2):
    """
    计算两个矩形框的交并比
    :param box1: list,第一个矩形框的左上角和右下角坐标
    :param box2: list,第二个矩形框的左上角和右下角坐标
    :return: 两个矩形框的交并比iou
    """
    x1 = max(box1[0], box2[0])  # 交集左上角x
    x2 = min(box1[2], box2[2])  # 交集右下角x
    y1 = max(box1[1], box2[1])  # 交集左上角y
    y2 = min(box1[3], box2[3])  # 交集右下角y
    overlap = max(0., x2 - x1) * max(0., y2 - y1)
    union = (box1[2] - box1[0]) * (box1[3] - box1[1]) \
            + (box2[2] - box2[0]) * (box2[3] - box2[1]) \
            - overlap
    return overlap / union

def bbox_iou_eval(box1, box2):
    box1 = np.array(box1).reshape(len(box1) >> 1, 2)
    poly1 = Polygon(box1).convex_hull #POLYGON ((0 0, 0 2, 2 2, 2 0, 0 0))
    print(type(mapping(poly1)['coordinates'])) # (((0.0, 0.0), (0.0, 2.0), (2.0, 2.0), (2.0, 0.0), (0.0, 0.0)),)
    box2 = np.array(box2).reshape(len(box2) >> 1, 2)
    poly2 = Polygon(box2).convex_hull
    if not poly1.intersects(poly2):  # 如果两四边形不相交
        iou = 0
    else:
        try:
            inter_area = poly1.intersection(poly2).area  # 相交面积
            iou = float(inter_area) / (poly1.area + poly2.area - inter_area)
        except shapely.geos.TopologicalError:
            print('shapely.geos.TopologicalError occured, iou set to 0')
            iou = 0
    return iou

if __name__ == '__main__':
    # box = [左上角x1,左上角y1,右下角x2,右下角y2]
    box1 = [10, 0, 15, 10]
    box2 = [12, 5, 20, 15]
    iou = IoU(box1, box2)
    print(iou)

    # box = [四个点的坐标，顺序无所谓]
    box3 = [0, 0, 2, 2, 2, 0, 0, 2]   # 左上，右上，右下，左下
    box4 = [1, 1, 1, 3, 3, 3, 3, 1]
    iou = bbox_iou_eval(box3, box4)
    print(iou)

    box3 = [0, 0, 2, 2, 2, 0, 0, 2]  # 左上，右上，右下，左下
    box4 = [1, 1, 1, 3, 3, 3, 3, 1]
    iou = polygon_iou(box3, box4)
    print(iou)

    box3 = [0, 0, 0, 1, 2, 2, 0, 1, 2, 0, 0, 2]  # 左上，右上，右下，左下
    box4 = [1, 1, 1, 3, 3, 3, 3, 1]
    iou = polygon_iou(box3, box4)
    print(iou)